﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Runtime.InteropServices;
using System.Globalization;
using System.Windows.Threading;
using LineEditTest;
using PSLib.CodeFormating.Lexems;

namespace StudioEditorTest.CodeEditor
{
	/// <summary>
	/// Interaction logic for CodeBlockPresenter.xaml
	/// </summary>
	public partial class CodeBlockPresenter : UserControl
	{
		#region Dependency properties

		// Using a DependencyProperty as the backing store for CursorPosition.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty CursorPositionProperty =
			DependencyProperty.Register("CursorPosition", typeof(int), typeof(CodeBlockPresenter), new UIPropertyMetadata(0, null));

		// Using a DependencyProperty as the backing store for LinePresenter.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty LinePresenterProperty =
			DependencyProperty.Register("LinePresenter", typeof(CodeLinePresenter), typeof(CodeBlockPresenter), new UIPropertyMetadata(null));

		#endregion

		#region Constructors

		public CodeBlockPresenter()
		{
			InitializeComponent();

			this.DataContextChanged += delegate(object sender, DependencyPropertyChangedEventArgs e)
			{
				CodeBlock newBlock = e.NewValue as CodeBlock;
				CodeBlock oldBlock = e.OldValue as CodeBlock;
				if (newBlock != null) newBlock.PropertyChanged += new PropertyChangedDelegate(newBlock_PropertyChanged);
				if (oldBlock != null) oldBlock.PropertyChanged -= new PropertyChangedDelegate(newBlock_PropertyChanged);
				UpdateWidth();
			};

			this.cursorBlinkTimer = new DispatcherTimer();
			this.cursorBlinkTimer.Tick += new EventHandler(cursorBlinkTimer_Tick);
			this.cursorBlinkTimer.Interval = TimeSpan.FromMilliseconds(700);
			this.cursorBlinkTimer.Start();
		}

		#endregion

		#region Event handlers

		void newBlock_PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
		{
			CodeBlock block = sender as CodeBlock;
			if (e.Property == CodeBlock.SourceProperty)
			{
				LexemList ll = ParseManager.Current.ParseSource(e.NewValue as string, false);
				if (string.IsNullOrEmpty(e.NewValue as string)) ReportInconsistency(LexemInconsistencyType.Empty);
				else if (ll.Count != 1) ReportInconsistency(LexemInconsistencyType.Splitted);
				else
				{
					if(block.LexemType != ll[0].Type) block.LexemType = ll[0].Type;
				}

				this.UpdateWidth();
			}
		}

		//protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
		//{
		//    base.OnPropertyChanged(e);
		//    if (e.Property == CodeBlockPresenter.CursorPositionProperty)
		//    {
		//        if ((int)e.NewValue < 0)
		//        {
		//            if (CursorOutOfRange != null) CursorOutOfRange(this, new CursorOutOfRangeArgs(LogicalDirection.Backward));
		//            this.CursorPosition = 0;
		//        }
		//        if ((int)e.NewValue > this.CodeBlock.Source.Length)
		//        {
		//            if (CursorOutOfRange != null) CursorOutOfRange(this, new CursorOutOfRangeArgs(LogicalDirection.Forward));
		//            this.CursorPosition = this.CodeBlock.Source.Length;
		//        }
		//    }
		//}

		private void cursorBlinkTimer_Tick(object sender, EventArgs e)
		{
			showCursor = !showCursor;
			this.InvalidateVisual();
		}

		protected override void OnRender(DrawingContext drawingContext)
		{
			FormattedText ft = GetFormatText(this.CodeBlock.Source);
			drawingContext.DrawText(ft,new Point(0,0));

			if (showCursor && this.CursorPosition != -1 && this.CursorPosition < this.CodeBlock.Source.Length)
			{
				Size position = MeasureText(this.CodeBlock.Source.Substring(0, this.CursorPosition));
				drawingContext.DrawLine(new Pen(Brushes.Black, 0.8), new Point(position.Width, 0), new Point(position.Width, position.Height));
			}
		}
		
		#endregion

		#region Private methods

		internal void ProcessKeyDown(KeyEventArgs e)
		{
			e.Handled = true;
			switch (e.Key)
			{
				case Key.Back:
				case Key.Delete:
					DeleteText(e.Key == Key.Back ? LogicalDirection.Backward : LogicalDirection.Forward);
					break;
				case Key.Left:
					this.CursorPosition--;
					break;
				case Key.Right:
					this.CursorPosition++;
					break;
				default:
					e.Handled = false;
					break;
			}
			if (!e.Handled)
			{
				string result = e.GetChar();
				if (!string.IsNullOrEmpty(result))
				{
					e.Handled = true;
					InsertText(LogicalDirection.Forward, result);
				}
			}

			if (e.Handled)
			{
				showCursor = true;
				this.InvalidateVisual();
			}
		}

		
		private void InsertText(LogicalDirection direction, string insertText)
		{
			int insertOffsetPosition = this.CursorPosition;
			int insertTextLenght = insertText.Length;
			string result = this.CodeBlock.Source.Substring(0, insertOffsetPosition);
			result += insertText;
			result += this.CodeBlock.Source.Substring(insertOffsetPosition, this.CodeBlock.Source.Length - insertOffsetPosition);
			this.CodeBlock.Source = result;

			if (direction == LogicalDirection.Forward) this.CursorPosition += insertTextLenght;
		}

		private void DeleteText(LogicalDirection direction)
		{
			int deleteOffsetPosition = this.CursorPosition;
			int deleteTextLenght = 1;

			string result = this.CodeBlock.Source;
			if (direction == LogicalDirection.Forward)
			{
				if (deleteOffsetPosition == result.Length) return;

				result = this.CodeBlock.Source.Substring(0, deleteOffsetPosition);
				result += this.CodeBlock.Source.Substring(deleteOffsetPosition + deleteTextLenght);
			}
			else
			{
				if (deleteOffsetPosition == 0) return;

				result = this.CodeBlock.Source.Substring(0, deleteOffsetPosition - deleteTextLenght);
				result += this.CodeBlock.Source.Substring(deleteOffsetPosition);
				this.CursorPosition -= deleteTextLenght;
			}

			this.CodeBlock.Source = result;
		}

		private void ReportInconsistency(LexemInconsistencyType type)
		{
			if (this.LinePresenter == null)
				this.LinePresenter = UITools.FindAncestor<CodeLinePresenter>(this);

			if(this.LinePresenter != null) this.LinePresenter.ReportLexemInconsistency(this,new CodeBlockLexemInconsistencyArgs(type));
		}

		#endregion

		#region Public methods

		public FormattedText GetFormatText(string text)
		{
			if (text == null) return null;

			FormattedText fText = new FormattedText(
					text,
					CultureInfo.CurrentCulture,
					this.FlowDirection,
					new Typeface(this.FontFamily,this.FontStyle,this.FontWeight,this.FontStretch),
					this.FontSize,
					this.Foreground);
			return fText;
		}

		public Size MeasureText(string text)
		{
			FormattedText ft = GetFormatText(text);
			if (ft == null) return Size.Empty;
			return new Size(ft.WidthIncludingTrailingWhitespace,ft.Height);
		}

		public void UpdateWidth()
		{
			this.Width = MeasureText(this.CodeBlock.Source).Width;
			this.InvalidateVisual();
		}

		#endregion

		#region Properties

		public int CursorPosition
		{
			get { return (int)this.CodeBlock.GetValue(CursorPositionProperty); }
			set { /*this.CodeBlock.SetValue(CursorPositionProperty, value);*/ }
		}

		public CodeLinePresenter LinePresenter
		{
			get { return (CodeLinePresenter)GetValue(LinePresenterProperty); }
			set { SetValue(LinePresenterProperty, value); }
		}
		
		public ListViewItem ListViewItem
		{
			get { return UITools.FindAncestor<ListViewItem>(this); }
		}

		public CodeBlock CodeBlock
		{
			get { return this.DataContext as CodeBlock; }
		}

		public CodeBlockPresenter PrevBlockPresenter
		{
			get 
			{
				int index = this.LinePresenter.ItemContainerGenerator.IndexFromContainer(this.ListViewItem);

				if (index - 1 >= 0)
				{
					ListViewItem prevItem = this.LinePresenter.ItemContainerGenerator.ContainerFromIndex(index - 1) as ListViewItem;
					return UITools.FindVisualChild<CodeBlockPresenter>(prevItem);
				}
				return null;
			}
		}

		public CodeBlockPresenter NextBlockPresenter
		{
			get
			{
				int index = this.LinePresenter.ItemContainerGenerator.IndexFromContainer(this.ListViewItem);

				if (index + 1 < this.LinePresenter.CodeLine.CodeBlocks.Count)
				{
					ListViewItem prevItem = this.LinePresenter.ItemContainerGenerator.ContainerFromIndex(index + 1) as ListViewItem;
					return UITools.FindVisualChild<CodeBlockPresenter>(prevItem);
				}
				return null;
			}
		}

		#endregion

		#region Events

		//public event EventHandler<CursorOutOfRangeArgs> CursorOutOfRange;

		#endregion

		private DispatcherTimer cursorBlinkTimer;
		private bool showCursor;
	}
}
